package com.beiding.cprender;


import com.beiding.render.*;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;

/*
    类路径引擎
    每个类路径都会有唯一一个类路径引擎
    类路径引擎类似于文件引擎
 */
public class CPTemplateEngine implements TemplateFinder, HandlerFinder, PluginFinder, ResourceFinder, RenderFilterHolder {

    private URLFinder resourceFinder;

    private TemplateCompiler templateCompiler;


    public CPTemplateEngine(File classpath) {

        //给定类路径
        try {
            this.resourceFinder = new URLFinder(classpath.toURI().toURL());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        init(ClasspathUtils.scanClass(classpath));
    }

    private Map<String, Handler> handlerMap = new HashMap<>();
    private Map<String, Handler> innerHandlerMap = new HashMap<>();
    private Map<Class, Handler> typeHandlerMap = new HashMap<>();
    private Map<String, Object> pluginMap = new HashMap<>();
    private Map<String, Object> innerPluginMap = new HashMap<>();
    private Map<Class, Object> typePluginMap = new HashMap<>();

    private Map<Class, CpTemplate> typeTemplateMap = new HashMap<>();
    private Map<String, CpTemplate> templateMap = new HashMap<>();
    private Map<String, Template> innerTemplateMap = new HashMap<>();

    private List<RenderFilter> renderFilters = new ArrayList<RenderFilter>() {
        {

            add(new RenderFilter() {
                @Override
                public void filter(This _this, NextFilterHolder nextFilterHolder) {
                    if (_this.isRoot()) {

                        if (!_this.getCommands().containsKey("plugins") && !_this.getCommands().containsKey("p")) {

                            //放入插件
                            innerPluginMap.forEach(_this::putIfAbsent);
                        }

                    }
                    nextFilterHolder.next();
                }

                @Override
                public int order() {
                    return Integer.MIN_VALUE;
                }
            });

            add(new RenderFilter() {
                @Override
                public void filter(This _this, NextFilterHolder nextFilterHolder) {
                    _this.render();
                }

                @Override
                public int order() {
                    return Integer.MAX_VALUE;
                }
            });
        }
    };


    private void init(Set<Class> classes) {

        templateCompiler = new TemplateCompiler();
        templateCompiler.setRenderTime(new RenderTime(this, this, this, this, renderFilters));

        for (Class c : classes) {

            if (c.isInterface()) {
                continue;
            }

            if (Modifier.isAbstract(c.getModifiers())) {
                continue;
            }


            boolean pass = true;
            for (Constructor constructor : c.getConstructors()) {
                if (constructor.getParameterCount() == 0) {
                    pass = false;
                    break;
                }
            }

            if (pass) {
                continue;
            }

            Object o = null;
            if (CpHandler.class.isAssignableFrom(c)) {
                //TODO 识别和处理

                try {
                    o = c.newInstance();
                    initInstance(o);
                    CpHandler handler = (CpHandler) o;
                    handlerMap.put(handler.name(), handler);
                    innerHandlerMap.put(handler.name(), handler);
                    typeHandlerMap.put(c, handler);
                } catch (IllegalAccessException | InstantiationException e) {
                    System.err.println("对象初始化失败,不可用:" + c);
                }
            }

            if (CpPlugin.class.isAssignableFrom(c)) {
                if (o == null) {
                    try {
                        o = c.newInstance();
                        initInstance(o);
                    } catch (IllegalAccessException | InstantiationException e) {
                        System.err.println("对象初始化失败,不可用:" + c);
                    }
                }
                if (o != null) {
                    CpPlugin classpathPlugin = (CpPlugin) o;
                    pluginMap.put(classpathPlugin.name(), classpathPlugin);
                    innerPluginMap.put(classpathPlugin.name(), classpathPlugin);
                    typePluginMap.put(c, classpathPlugin);
                }
            }

            if (CpTemplate.class.isAssignableFrom(c)) {
                if (o == null) {
                    try {
                        o = c.newInstance();
                        initInstance(o);
                    } catch (IllegalAccessException | InstantiationException e) {
                        System.err.println("对象初始化失败,不可用:" + c);
                    }
                }
                if (o != null) {
                    CpTemplate cpTemplate = (CpTemplate) o;
                    String name = cpTemplate.name();
                    String coord = "/" + name.replace(".", "/");
                    Template innerTemplate = getInnerTemplate(coord);
                    if (innerTemplate == null) {
                        System.out.println("模板初始化失败:" + coord);
                    } else {
                        cpTemplate.setTarget(innerTemplate);
                        innerTemplateMap.put(coord, innerTemplate);
                        templateMap.put(coord, cpTemplate);
                        typeTemplateMap.put(c, cpTemplate);
                    }

                }
            }

            if (CpImporter.class.isAssignableFrom(c)) {
                if (o == null) {
                    try {
                        o = c.newInstance();
                    } catch (IllegalAccessException | InstantiationException e) {
                        System.err.println("对象初始化失败,不可用:" + c);
                    }
                }
                if (o != null) {
                    CpImporter importer = (CpImporter) o;
                    Map<String, Class<? extends CpExporter>> imports = importer.imports();

                    imports.forEach((namespace, exporter) -> {

                        try {
                            //TODO 获取所有的
                            CpExporter exporterInstance = exporter.newInstance();
                            CPTemplateEngine engine = F.getEngine(exporter);
                            if (namespace == null || namespace.trim().length() == 0) {
                                throw new RuntimeException("命名空间不能为空:" + c);
                            }

                            List<Class> classList = null;
                            if (exporterInstance.export() != null) {
                                classList = Arrays.asList(exporterInstance.export());
                            }

                    /*
                        handler/plugin/template 三者具有不同的命名策略
                        handler
                     */
                            List<Class> finalClassList = classList;
                            engine.templateMap.forEach((k, v) -> {
                                if (finalClassList == null || finalClassList.contains(v.getClass())) {
                                    String t = "/" + namespace + k;
                                    // this.templateMap.put(t, v);
                                    this.innerTemplateMap.put(t, v);
                                }
                            });

                            engine.handlerMap.forEach((k, v) -> {
                                if (finalClassList == null || finalClassList.contains(v.getClass()))
                                    this.innerHandlerMap.put(namespace + "." + k, v);
                            });

                            Map map = new HashMap();

                            engine.pluginMap.forEach((k, v) -> {
                                if (finalClassList == null || finalClassList.contains(v.getClass()))
                                    map.put(k, v);
                            });

                            this.innerPluginMap.put(namespace, map);

                        } catch (IllegalAccessException | InstantiationException e) {
                            System.out.println("导出器初始化失败:" + exporter);
                        }
                    });


                }
            }

        }

    }

    private Template getInnerTemplate(String coord) {
        //TODO 处理内部模板
        String resource = "template" + coord + ".xml";
        Resource r = findResource(resource);
        if (r == null) {
            return null;
        } else {
            try {
                String read = r.read();
                return this.templateCompiler.compile(read, coord);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    //初始化操作,对其中模板预定的字段进行赋值
    private void initInstance(Object object) {

        Field[] declaredFields = object.getClass().getDeclaredFields();

        for (Field declaredField : declaredFields) {
            if (declaredField.getName().equals("$engine") && declaredField.getType().isAssignableFrom(CPTemplateEngine.class)) {

                declaredField.setAccessible(true);
                try {
                    declaredField.set(object, this);
                } catch (Exception ignore) {

                }
            }
        }

    }


    @Override
    public Template findTemplate(String relative, String expression) {
        String absolute = "/" + expression.replace(".", "/");
        if (innerTemplateMap.containsKey(absolute)) {
            return innerTemplateMap.get(absolute);
        }
        Template innerTemplate = getInnerTemplate(absolute);
        innerTemplateMap.put(absolute, innerTemplate);
        return innerTemplate;
    }


    //TODO 不处理相对路径
    @Override
    public Handler findHandler(String relative, String path) {
        path = path.trim();
        return innerHandlerMap.get(path);
    }

    @Override
    public Map<String, Object> findPluginBeans(String relative, String plugins) {
        Map<String, Object> objectMap = new HashMap<>();
        String[] split = plugins.split(",");
        for (String o : split) {
            Object pg = innerPluginMap.get(o);
            if (pg != null) {
                objectMap.put(o, pg);
            }
        }
        return objectMap;
    }

    @Override
    public Resource findResource(String relative, String path) {//只能搜索
        return findResource(CoordinateUtils.relativeToAbsolute(relative, path));
    }

    public Resource findResource(String path) {
        URL rs = resourceFinder.find(path);
        if (rs == null) {
            return null;
        }
        return new CpResource(rs);
    }

    public <T extends CpTemplate> T getTemplate(Class<T> clz) {
        return (T) typeTemplateMap.get(clz);
    }

    public <T extends CpHandler> T getHandler(Class<T> clz) {
        return (T) typeHandlerMap.get(clz);
    }

    public <T extends CpPlugin> T getPlugin(Class<T> clz) {
        return (T) typePluginMap.get(clz);
    }

    @Override
    public List<RenderFilter> getFilters() {
        return renderFilters;
    }

    @Override
    public void addFilter(RenderFilter filter) {
        renderFilters.add(filter);
    }


}
